﻿using System;
using IInject.Properties;

namespace IInject.Registration.Scanning
{
	public abstract class ScanningRule : IScanningRule
    {
        #region Constructors

        protected ScanningRule()
        {
        }

		#endregion

		#region Properties

		/// <summary>
		/// Gets the type of scanning rule.
		/// </summary>
		/// <value>
		/// The type.
		/// </value>
		public abstract ScanningRuleType Type { get; }

		#endregion

        #region Methods

        /// <summary>
        /// Returns a rule that determines if a type inherits the provided type.
        /// </summary>
        /// <typeparam name="T">The type the scanned type inherits.</typeparam>
        /// <returns>A rule that determines if a type inherits the provided type.</returns>
		public static IScanningRule Inherits<T>() where T : class
        {
            return new BaseTypeScanningRule(typeof(T));
        }

        /// <summary>
        /// Returns a rule that determines if a type inherits the provided type.
        /// </summary>
        /// <param name="baseType">The type the scanned type inherits.</param>
        /// <returns>A rule that determines if a type inherits the provided type.</returns>
		public static IScanningRule Inherits(Type baseType)
        {
            return new BaseTypeScanningRule(baseType);
        }

        /// <summary>
        /// Returns a rule that determines if a type implements the provided interface type.
        /// </summary>
        /// <typeparam name="TInterface">The interface type the scanned type implement.</param>
        /// <returns>A rule that determines if a type implements the provided interface type.</returns>
        public static IScanningRule Implements<TInterface>()
        {
            return Implements(typeof(TInterface));
        }

        /// <summary>
        /// Returns a rule that determines if a type implements the provided interface type.
        /// </summary>
        /// <param name="interfaceType">The interface type the scanned type implements.</param>
        /// <returns>A rule that determines if a type implements the provided interface type.</returns>
        public static IScanningRule Implements(Type interfaceType)
        {
            if (!interfaceType.IsInterface)
            {
                throw new ArgumentException(string.Format(Resources.TypeMustBeAnInterfaceExceptionMessage, interfaceType.FullName));
            }

            return new InterfaceScanningRule(interfaceType);
        }

        /// <summary>
        /// Returns a rule to determine if the type implements
        /// an interface with the default naming convention.
        /// Example: class MyClass implements interface IMyClass
        /// </summary>
        /// <returns>A default interface implementation rule.</returns>
        public static IScanningRule InterfaceNamingDefault()
        {
            return new InterfaceNamingScanningRule(Resources.InterfaceNamingConventionPrefix);
        }

        /// <summary>
        /// Returns a rule that checks if the type is a public non-abstract class.
        /// </summary>
        /// <returns>A rule that checks if the type is a public non-abstract class.</returns>
        public static IScanningRule PublicClass()
        {
            return new PublicClassScanningRule();
        }

        public abstract MatchingResult IsMatch(Type type);

        #endregion
	}
}
